Skip to content

feat: route profile URLs to njump.me for non-Divine users#47

Merged
NotThatKindOfDrLiz merged 15 commits intomainfrom
feat/profile-url-routing
Apr 8, 2026
Merged

feat: route profile URLs to njump.me for non-Divine users#47
NotThatKindOfDrLiz merged 15 commits intomainfrom
feat/profile-url-routing

Conversation

@mbradley
Copy link
Copy Markdown
Member

@mbradley mbradley commented Apr 7, 2026

Summary

  • Profile links in moderation UI now route to njump.me/{npub} when the user isn't indexed by Funnelcake (i.e., their profile doesn't exist on relay.divine.video). Previously all links went to divine.video/profile/{npub}, which is blank for non-Divine Nostr users.
  • useAuthor hook now exposes isFunnelcakeUser: boolean tracking whether the Funnelcake REST path succeeded. Zero new network requests -- just reads the result already being fetched.
  • Category A call sites updated: UserIdentifier, UserDisplayName, ReporterCard, ReporterInline, ReportedBy, UserProfilePreview. Category B (thread, events list, banned cards) deferred -- no regression, lower priority.

Surfaced by Aleysha's Apr 2 report batch -- moderators were clicking through to empty Divine profile pages for users who only exist on external relays.

Test plan

  • Deploy to staging, open a report from a non-Divine Nostr user, confirm profile link goes to njump.me
  • Open a report from a known Divine user, confirm profile link still goes to divine.video
  • Verify tooltip on ReporterInline shows "View profile on njump.me" vs "View profile on Divine" appropriately
  • Existing tests: 71 pass, tsc clean

useAuthor now tracks whether Funnelcake REST succeeded (isFunnelcakeUser).
Profile links in report views use divine.video when the user is indexed
by Funnelcake, njump.me otherwise. Avoids sending moderators to empty
profile pages for Nostr users who never used Divine.

Category A call sites updated: UserIdentifier, ReporterCard, ReporterInfo,
UserProfilePreview. Category B deferred (no regression).
@mbradley mbradley force-pushed the feat/profile-url-routing branch from 7ad17e2 to 9d9b1f6 Compare April 7, 2026 21:09
mbradley added 12 commits April 7, 2026 18:15
Shows a small ExternalLink icon next to usernames when the profile link
goes to njump.me instead of divine.video. Gives moderators an at-a-glance
signal that the reporter or reported user is not on our relay.
…ents

Category B components now use getProfileUrl with isFunnelcakeUser:
- ThreadContext PostCard: external icon on thread ancestor authors
- ThreadModal ThreadPost: external icon on full thread post authors
- EventsList EventRow: external icon on event list authors
- BannedUserCard: defaults to njump (no Funnelcake signal available)
- UserProfileCard: defaults to njump (receives props, no hook)

All getDivineProfileUrl call sites migrated. Function kept in constants
for backward compatibility.
ExternalLink arrow was ambiguous (looks like a generic link indicator).
Globe icon in purple clearly signals 'broader Nostr network'. Added
'Nostr' badge on ReporterInline for reporters, matching the existing
trust-level badge pattern.
Recent Content now fetches videos (34235/34236), comments (1111), and
text notes (1) instead of just kind 1. Each item shows a type badge:
- Video (green) — visible in Divine apps
- Comment (amber) — visible in Divine apps
- Note (amber + warning) — 'Text note — not visible in Divine apps'

Each item links to divine.video (videos) or njump.me (notes/comments)
for context. Helps moderators understand what content is on the relay
and whether Divine users can actually see it.
Both badges now link to the user's profile:
- 'Divine' (green) links to divine.video/profile
- 'Nostr' (purple) links to njump.me
Shown on both reporter and reported user sections.
UserProfileCard (the actual reported user component in report detail)
now receives isFunnelcakeUser from useReportContext. Shows green 'Divine'
or purple 'Nostr' badge next to the user name, both clickable.

Recent Content section now shows kind-aware badges:
- Video (green) for kind 34235/34236
- Comment (amber) for kind 1111
- Note (amber + globe) for kind 1, with 'not visible in Divine apps' warning
Each item links to divine.video (videos) or njump.me (notes).
Video: 'Short-form video — visible in Divine apps'
Comment: 'Video comment — visible in Divine apps'
Note: 'Text note (kind 1) — not visible in Divine apps. Only visible via external Nostr clients.'

Removes the separate warning line under notes. Comment badge
color changed to green (visible in Divine) to match Video.
Radix UI tooltips weren't appearing in Safari. Native HTML title
attribute works reliably across all browsers and is accessible
by default (read by screen readers as accessible description).
Previously most call sites passed useAuthor(pubkey) without apiUrl,
so Funnelcake REST was never tried and isFunnelcakeUser was always
false. Now the hook reads config.apiUrl from useAppContext as fallback,
so all call sites get accurate Funnelcake detection without needing
to pass apiUrl explicitly. Explicit override still supported.
@mbradley
Copy link
Copy Markdown
Member Author

mbradley commented Apr 8, 2026

Code review

Found 1 issue:

  1. ReporterInfo hardcodes isFunnelcakeUser to false — reporters who are Divine users always get routed to njump.me. ReportedBy in the same file correctly reads isFunnelcakeUser from useAuthor, but ReporterInfo receives profile/pubkey as props without calling useAuthor. Fix: either call useAuthor(pubkey) inside ReporterInfo, or add isFunnelcakeUser to ReporterInfoProps and thread it from the caller.

const trust = getTrustLevel(reportCount);
const profileUrl = getProfileUrl(npub, false);

… false

ReporterInfo was hardcoding getProfileUrl(npub, false), routing all
reporters to njump.me. ReportedBy in the same file correctly reads
isFunnelcakeUser from useAuthor -- now ReporterInfo does too.
@mbradley mbradley marked this pull request as ready for review April 8, 2026 20:33
Copy link
Copy Markdown
Member

@NotThatKindOfDrLiz NotThatKindOfDrLiz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the direction here. The core useAuthor + getProfileUrl approach is clean, and routing non-Divine users to njump.me solves a real moderator problem.

I do want to request two fixes before merge.

First, useAuthor now implicitly depends on the current environment's apiUrl, but the query key in src/hooks/useAuthor.ts still only keys on pubkey:

queryKey: ['author', pubkey ?? ''],

That was fine before, but after this change callers that omit the override now fall back to config.apiUrl. If a moderator switches environments, TanStack Query can reuse a cached author result from the previous environment for the same pubkey, including the new isFunnelcakeUser flag. That creates exactly the kind of subtle misrouting this PR is trying to fix: profile links can point to the wrong destination for up to the cache lifetime. Please include apiUrl in the query key.

Second, the PR introduces new hardcoded production links for recent-content event URLs in UserProfileCard and UserProfilePreview:

https://divine.video/${...}

This repo's rule is that domains come from config, not application code, and this is more than stylistic: in staging or local, these links will kick moderators out to production. ThreadContext already has the same hardcoded pattern, but this PR adds more of it, so I don't think we should expand that further. Please route these through an environment-aware helper instead of hardcoding divine.video.

A couple of quick notes on the rest:

  • The ReporterInfo issue looks fixed on the current head.
  • The broader recent-content UI work is useful, but it does make the PR a bit wider than the title suggests.

Requesting changes for the useAuthor cache key and the hardcoded production event links. Once those are fixed, I'm comfortable taking another look.

@NotThatKindOfDrLiz
Copy link
Copy Markdown
Member

Picked this up and pushed the follow-up fix set on top of the branch (4871928).

What changed:

  • useAuthor is now keyed by apiUrl, so environment switches do not reuse stale isFunnelcakeUser results from another environment.
  • Public event links are now centralized through a helper instead of hardcoding new divine.video URLs in the UI. Production still routes to Divine; staging/local fall back to njump so moderators do not get kicked to production unexpectedly.
  • Removed the two plan-doc files from the PR so the diff stays focused on the feature work.

Local verification:

  • npx tsc -p tsconfig.app.json --noEmit
  • npx eslint src/components/ThreadContext.tsx src/components/UserProfileCard.tsx src/components/UserProfilePreview.tsx src/hooks/useAuthor.ts src/lib/constants.ts
  • npx vite build

Copy link
Copy Markdown
Member

@NotThatKindOfDrLiz NotThatKindOfDrLiz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The follow-up fixes are in. The environment-sensitive caching and public-link routing issues are resolved, the PR diff is tighter, and the frontend checks I ran are clean.

@NotThatKindOfDrLiz NotThatKindOfDrLiz merged commit d04d9e4 into main Apr 8, 2026
2 checks passed
@NotThatKindOfDrLiz NotThatKindOfDrLiz self-requested a review April 8, 2026 21:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants